/*****************************************************************************
* | File        :   LCD_1in14.C
* | Function    :   test Demo
* | Info        :
*----------------
* | This version:   V1.0
* | Date        :   2021-03-16
* | Info        :
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documnetation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to  whom the Software is
# furished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
******************************************************************************/

#include <drv_spi.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <logo.h>
#include <pin_dev_cfg.h>
#include <thread_cfg.h>
#include <module_usb.h>

#include "module_LCD_1in14.h"
#include <stdlib.h>     //itoa()
#include <stdio.h>

static LCD_1IN14_ATTRIBUTES_t LCD_1IN14;

/**
 * #define DISPLAY_SPI_NAME                "spi2"
 * #define PIN_DISPLAY_SPI_MOSI            54
 * #define PIN_DISPLAY_SPI_SCK             52
 * #define PIN_DISPLAY_SPI_CS              51
 * #define PIN_DISPLAY_DC                  55 //PD8
 * #define PIN_DISPLAY_RST                 56
 *
 */
extern rt_event_t p_usb_event;

static  rt_thread_t thread_display;
struct rt_spi_device* dev_spi_display;

/******************************************************************************
function :  Hardware reset
parameter:
******************************************************************************/
static void LCD_1IN14_Reset(void)
{
    rt_pin_write(PIN_DISPLAY_RST, PIN_HIGH);
    rt_thread_mdelay(100);
    rt_pin_write(PIN_DISPLAY_RST, PIN_LOW);
    rt_thread_mdelay(100);
    rt_pin_write(PIN_DISPLAY_RST, PIN_HIGH);
    rt_thread_mdelay(100);
}

/******************************************************************************
function :  send command
parameter:
     Reg : Command register
******************************************************************************/
static void LCD_1IN14_SendCommand(uint8_t Reg)
{
    rt_pin_write(PIN_DISPLAY_DC, PIN_LOW);
    rt_pin_write(PIN_DISPLAY_SPI_CS, PIN_LOW);
    DEV_SPI_WriteByte(Reg);
    rt_pin_write(PIN_DISPLAY_SPI_CS, PIN_HIGH);
}

/******************************************************************************
function :  send data
parameter:
    Data : Write data
******************************************************************************/
static void LCD_1IN14_SendData_8Bit(uint8_t Data)
{
    rt_pin_write(PIN_DISPLAY_DC, PIN_HIGH);
    rt_pin_write(PIN_DISPLAY_SPI_CS, PIN_LOW);
    DEV_SPI_WriteByte(Data);
    rt_pin_write(PIN_DISPLAY_SPI_CS, PIN_HIGH);
}

/******************************************************************************
function :  send data
parameter:
    Data : Write data
******************************************************************************/
static void LCD_1IN14_SendData_16Bit(rt_uint16_t Data)
{
    rt_pin_write(PIN_DISPLAY_DC, PIN_HIGH);
    rt_pin_write(PIN_DISPLAY_SPI_CS, PIN_LOW);
    DEV_SPI_WriteByte((Data >> 8) & 0xFF);
    DEV_SPI_WriteByte(Data & 0xFF);
    rt_pin_write(PIN_DISPLAY_SPI_CS, PIN_HIGH);
}

/******************************************************************************
function :  Initialize the lcd register
parameter:
******************************************************************************/
static void LCD_1IN14_InitReg(void)
{
    LCD_1IN14_SendCommand(0x3A);
    LCD_1IN14_SendData_8Bit(0x05);
    LCD_1IN14_SendCommand(0xB2);
    LCD_1IN14_SendData_8Bit(0x0C);
    LCD_1IN14_SendData_8Bit(0x0C);
    LCD_1IN14_SendData_8Bit(0x00);
    LCD_1IN14_SendData_8Bit(0x33);
    LCD_1IN14_SendData_8Bit(0x33);
    LCD_1IN14_SendCommand(0xB7);  //Gate Control
    LCD_1IN14_SendData_8Bit(0x35);
    LCD_1IN14_SendCommand(0xBB);  //VCOM Setting
    LCD_1IN14_SendData_8Bit(0x19);
    LCD_1IN14_SendCommand(0xC0); //LCM Control
    LCD_1IN14_SendData_8Bit(0x2C);
    LCD_1IN14_SendCommand(0xC2);  //VDV and VRH Command Enable
    LCD_1IN14_SendData_8Bit(0x01);
    LCD_1IN14_SendCommand(0xC3);  //VRH Set
    LCD_1IN14_SendData_8Bit(0x12);
    LCD_1IN14_SendCommand(0xC4);  //VDV Set
    LCD_1IN14_SendData_8Bit(0x20);
    LCD_1IN14_SendCommand(0xC6);  //Frame Rate Control in Normal Mode
    LCD_1IN14_SendData_8Bit(0x0F);
    LCD_1IN14_SendCommand(0xD0);  // Power Control 1
    LCD_1IN14_SendData_8Bit(0xA4);
    LCD_1IN14_SendData_8Bit(0xA1);
    LCD_1IN14_SendCommand(0xE0);  //Positive Voltage Gamma Control
    LCD_1IN14_SendData_8Bit(0xD0);
    LCD_1IN14_SendData_8Bit(0x04);
    LCD_1IN14_SendData_8Bit(0x0D);
    LCD_1IN14_SendData_8Bit(0x11);
    LCD_1IN14_SendData_8Bit(0x13);
    LCD_1IN14_SendData_8Bit(0x2B);
    LCD_1IN14_SendData_8Bit(0x3F);
    LCD_1IN14_SendData_8Bit(0x54);
    LCD_1IN14_SendData_8Bit(0x4C);
    LCD_1IN14_SendData_8Bit(0x18);
    LCD_1IN14_SendData_8Bit(0x0D);
    LCD_1IN14_SendData_8Bit(0x0B);
    LCD_1IN14_SendData_8Bit(0x1F);
    LCD_1IN14_SendData_8Bit(0x23);
    LCD_1IN14_SendCommand(0xE1);  //Negative Voltage Gamma Control
    LCD_1IN14_SendData_8Bit(0xD0);
    LCD_1IN14_SendData_8Bit(0x04);
    LCD_1IN14_SendData_8Bit(0x0C);
    LCD_1IN14_SendData_8Bit(0x11);
    LCD_1IN14_SendData_8Bit(0x13);
    LCD_1IN14_SendData_8Bit(0x2C);
    LCD_1IN14_SendData_8Bit(0x3F);
    LCD_1IN14_SendData_8Bit(0x44);
    LCD_1IN14_SendData_8Bit(0x51);
    LCD_1IN14_SendData_8Bit(0x2F);
    LCD_1IN14_SendData_8Bit(0x1F);
    LCD_1IN14_SendData_8Bit(0x1F);
    LCD_1IN14_SendData_8Bit(0x20);
    LCD_1IN14_SendData_8Bit(0x23);
    LCD_1IN14_SendCommand(0x21);  //Display Inversion On
    LCD_1IN14_SendCommand(0x11);  //Sleep Out
    LCD_1IN14_SendCommand(0x29);  //Display On
}

/********************************************************************************
function:   Set the resolution and scanning method of the screen
parameter:
        Scan_dir:   Scan direction
********************************************************************************/
static void LCD_1IN14_SetAttributes(uint8_t Scan_dir)
{
    //Get the screen scan direction
    LCD_1IN14.SCAN_DIR = Scan_dir;
    uint8_t MemoryAccessReg = 0x00;

    //Get GRAM and LCD width and height
    if(Scan_dir == HORIZONTAL)
    {
        LCD_1IN14.HEIGHT    = LCD_1IN14_WIDTH;
        LCD_1IN14.WIDTH   = LCD_1IN14_HEIGHT;
        MemoryAccessReg = 0X70;
    }
    else
    {
        LCD_1IN14.HEIGHT    = LCD_1IN14_HEIGHT;
        LCD_1IN14.WIDTH   = LCD_1IN14_WIDTH;
        MemoryAccessReg = 0X00;
    }

    // Set the read / write scan direction of the frame memory
    LCD_1IN14_SendCommand(0x36); //MX, MY, RGB mode
    LCD_1IN14_SendData_8Bit(MemoryAccessReg);   //0x08 set RGB
}

/********************************************************************************
function :  Initialize the lcd
parameter:
********************************************************************************/
void thread_display_Init(void)
{
    // DEV_SET_PWM(90);
    //Hardware reset
    rt_hw_spi_device_attach(DISPLAY_SPI_BUS_NAME, DISPLAY_SPI_NAME, GPIOB, GPIO_PIN_11);
    /* 查找 spi 设备获取设备句柄 */
    dev_spi_display = (struct rt_spi_device*)rt_device_find(DISPLAY_SPI_NAME);

    if(dev_spi_display == RT_NULL)
    {
        rt_kprintf("spi sample run failed! can't find %s device!\n", DISPLAY_SPI_NAME);
    }
    else
    {
        struct rt_spi_configuration spi_config;
        spi_config.data_width = 8;
        spi_config.max_hz = 50 * 1000 * 1000;
        spi_config.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
        rt_spi_configure(dev_spi_display, &spi_config);
    }

    //Add thread
    thread_display = rt_thread_create("tdisplay",
                                      thread_display_entry,
                                      RT_NULL,
                                      THREAD_STACK_SIZE_LARGE,
                                      THREAD_PRIORITY_LV4,
                                      THREAD_TIMESLICE_MIDDLE);

    /* 如果获得线程控制块，启动这个线程 */
    if(thread_display != RT_NULL)
    {
        rt_thread_startup(thread_display);
    }
}
INIT_APP_EXPORT(thread_display_Init);

static void thread_display_entry(void* parameter)
{
    uint8_t* temp;
    uint32_t event;
    LCD_1IN14_Reset();
    //Set the resolution and scanning method of the screen
    LCD_1IN14_SetAttributes(HORIZONTAL);
    //Set the initialization register
    LCD_1IN14_InitReg();
    rt_thread_mdelay(500);
    // temp = (uint8_t*)capslock_on_data;
    temp = (uint8_t*)rtt_logo_data;
    LCD_1IN14_Display(temp);

    while(1)
    {
        // LCD_1IN14_Clear(0xff);
        temp = (uint8_t*)rtt_logo_data;
        LCD_1IN14_Display(temp);

        if(rt_event_recv(p_usb_event, (EVENT_CAPSLOCK_ON | EVENT_CAPSLOCK_OFF | EVENT_NUMSLOCK_ON | EVENT_NUMSLOCK_OFF),
                         RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
                         RT_WAITING_FOREVER, &event) == RT_EOK)
        {
            LCD_1IN14_Clear(0xff);

            if(event & EVENT_CAPSLOCK_ON)
            {
                temp = (uint8_t*)capslock_on_data;
                LCD_1IN14_Display(temp);
                // LCD_1IN14_DisplayWindows(20, (LCD_1IN14_HEIGHT - 96) / 2, (LCD_1IN14_WIDTH + 96) / 2, (LCD_1IN14_HEIGHT + 96) / 2, temp);
            }
            // else if(event & EVENT_CAPSLOCK_OFF)
            // {
            //     temp = (uint8_t*)capslock_off_data;
            //     LCD_1IN14_Display(temp);
            // }

            rt_thread_mdelay(1000);
        }

        // LCD_1IN14_Clear(0xff);
        rt_thread_mdelay(500);
    }
}



/********************************************************************************
function:   Sets the start position and size of the display area
parameter:
        Xstart  :   X direction Start coordinates
        Ystart  :   Y direction Start coordinates
        Xend    :   X direction end coordinates
        Yend    :   Y direction end coordinates
********************************************************************************/
void LCD_1IN14_SetWindows(rt_uint16_t Xstart, rt_uint16_t Ystart, rt_uint16_t Xend,
                          rt_uint16_t Yend)
{
    uint8_t x, y;

    if(LCD_1IN14.SCAN_DIR == HORIZONTAL) {x = 40; y = 53;}
    else { x = 52; y = 40; }

    //set the X coordinates
    LCD_1IN14_SendCommand(0x2A);
    LCD_1IN14_SendData_16Bit(Xstart + x);
    LCD_1IN14_SendData_16Bit(Xend - 1 + x);
    //set the Y coordinates
    LCD_1IN14_SendCommand(0x2B);
    LCD_1IN14_SendData_16Bit(Ystart + y);
    LCD_1IN14_SendData_16Bit(Yend - 1   + y);
    LCD_1IN14_SendCommand(0X2C);
    // printf("%d %d\r\n",x,y);
}

/******************************************************************************
function :  Clear screen
parameter:
******************************************************************************/
void LCD_1IN14_Clear(rt_uint16_t Color)
{
    rt_uint16_t j, i;
    rt_uint16_t Image[LCD_1IN14.WIDTH * LCD_1IN14.HEIGHT];
    Color = ((Color << 8) & 0xff00) | (Color >> 8);

    for(j = 0; j < LCD_1IN14.HEIGHT * LCD_1IN14.WIDTH; j++)
    {
        Image[j] = Color;
    }

    LCD_1IN14_SetWindows(0, 0, LCD_1IN14.WIDTH, LCD_1IN14.HEIGHT);
    rt_pin_write(PIN_DISPLAY_DC, PIN_HIGH);
    rt_pin_write(PIN_DISPLAY_SPI_CS, PIN_LOW);
    rt_kprintf("HEIGHT %d, WIDTH %d\r\n", LCD_1IN14.HEIGHT, LCD_1IN14.WIDTH);

    for(j = 0; j < LCD_1IN14.HEIGHT; j++)
    {
        DEV_SPI_Write_nByte((uint8_t*)&Image[j * LCD_1IN14.WIDTH], LCD_1IN14.WIDTH*2);
    }

    rt_pin_write(PIN_DISPLAY_SPI_CS, PIN_HIGH);
}

/******************************************************************************
function :  Sends the image buffer in RAM to displays
parameter:
******************************************************************************/
void LCD_1IN14_Display(rt_uint8_t* Image)
{
    rt_uint16_t j;
    LCD_1IN14_SetWindows(0, 0, LCD_1IN14.WIDTH, LCD_1IN14.HEIGHT);
    rt_pin_write(PIN_DISPLAY_DC, PIN_HIGH);
    rt_pin_write(PIN_DISPLAY_SPI_CS, PIN_LOW);

    for(j = 0; j < LCD_1IN14.HEIGHT * 2; j++)
    {
        DEV_SPI_Write_nByte((uint8_t*)&Image[j * LCD_1IN14.WIDTH], LCD_1IN14.WIDTH);
    }

    rt_pin_write(PIN_DISPLAY_SPI_CS, PIN_HIGH);
    LCD_1IN14_SendCommand(0x29);
}

/******************************************************************************
function :  Sends the image buffer in RAM to displays
parameter:
        Xstart  :   X direction Start coordinates
        Ystart  :   Y direction Start coordinates
        Xend    :   X direction end coordinates
        Yend    :   Y direction end coordinates
        Image   :   Written content
******************************************************************************/
void LCD_1IN14_DisplayWindows(rt_uint16_t Xstart, rt_uint16_t Ystart, rt_uint16_t Xend,
                              rt_uint16_t Yend, rt_uint8_t* Image)
{
    // display
    uint32_t Addr = 0;
    rt_uint16_t j;
    LCD_1IN14_SetWindows(Xstart, Ystart, Xend, Yend);
    rt_pin_write(PIN_DISPLAY_DC, PIN_HIGH);
    rt_pin_write(PIN_DISPLAY_SPI_CS, PIN_LOW);

    for(j = Ystart; j < Yend - 1; j++)
    {
        Addr = Xstart + j * LCD_1IN14.WIDTH ;
        DEV_SPI_Write_nByte((uint8_t*)&Image[Addr], (Xend - Xstart) * 2);
    }

    rt_pin_write(PIN_DISPLAY_SPI_CS, PIN_HIGH);
}

/******************************************************************************
function :  Change the color of a point
parameter:
        X       :   X coordinates
        Y       :   Y coordinates
        Color   :   Color
******************************************************************************/
void LCD_1IN14_DisplayPoint(rt_uint16_t X, rt_uint16_t Y, rt_uint16_t Color)
{
    LCD_1IN14_SetWindows(X, Y, X, Y);
    LCD_1IN14_SendData_16Bit(Color);
}

void DEV_SPI_WriteByte(uint8_t Value)
{
    rt_spi_transfer(dev_spi_display, &Value, RT_NULL, 1);
}

void DEV_SPI_Write_nByte(uint8_t pData[], uint32_t Len)
{
    rt_spi_send(dev_spi_display, pData, Len);
}

